package com.mk.util.pay;

import act.apidoc.Description;
import act.app.ActionContext;
import act.inject.DefaultValue;
import com.mk.exception.UserException;
import org.osgl.util.S;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@SuppressWarnings("all")
public abstract class PayUtils {

    public static Map<String, String> getNotifyParam(Map<String, String[]> notifyParams) {
        Map<String, String> result = new HashMap<>();
        notifyParams.forEach((k, v) -> {
            if (v.length > 0) {
                result.put(k, v[0]);
            }
        });
        return result;
    }
    public static String returnWxXML() {
        return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    }
    public static PayUtils getPayUtils(@Description("支付类型") @DefaultValue("WX") String payType) {
        PayUtils payUtils = null;
        try {
            payUtils = (PayUtils) PayUtils.payFactory(payType).newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        if (payUtils == null) {
            throw new UserException("支付方式错误");
        }
        return payUtils;
    }

    /**
     * 支付对象获取
     *
     * @param payType
     * @return
     */
    public static Class payFactory(String payType) {
        Class obj = null;
        if (S.isEmpty(payType)) {
            throw new UserException("请选择支付类型");
        }
        switch (payType) {
            case "WX":
                obj = WeixinPayUtils.class;
                break;
            case "UN":
                obj = UnionPayUtils.class;
                break;
            default:
                break;
        }
        return obj;
    }

    /**
     * 解析返回的xml文件
     *
     * @param xmlString
     * @return
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public static Map<String, String> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {

        //这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //region 防止XXE漏洞
        String FEATURE = null;
        try {
            // This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented
            // Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
            FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
            factory.setFeature(FEATURE, true);
            // If you can't completely disable DTDs, then at least do the following:
            // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
            // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
            // JDK7+ - http://xml.org/sax/features/external-general-entities
            FEATURE = "http://xml.org/sax/features/external-general-entities";
            factory.setFeature(FEATURE, false);
            // Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
            // Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
            // JDK7+ - http://xml.org/sax/features/external-parameter-entities
            FEATURE = "http://xml.org/sax/features/external-parameter-entities";
            factory.setFeature(FEATURE, false);

            // Disable external DTDs as well
            FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
            factory.setFeature(FEATURE, false);
            // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
            factory.setXIncludeAware(false);
            factory.setExpandEntityReferences(false);
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }
        //endregion 防止XXE漏洞
        DocumentBuilder builder = factory.newDocumentBuilder();
        InputStream is = new ByteArrayInputStream(xmlString.getBytes(StandardCharsets.UTF_8));
        Document document = builder.parse(is);

        //获取到document里面的全部结点
        NodeList allNodes = document.getFirstChild().getChildNodes();
        Node node;
        Map<String, String> map = new HashMap<>();
        int i = 0;
        while (i < allNodes.getLength()) {
            node = allNodes.item(i);
            if (node instanceof Element) {
                map.put(node.getNodeName(), node.getTextContent());
            }
            i++;
        }
        return map;

    }

    public static Map<String, String> getMapFromBr(BufferedReader reader) throws Exception {
        String line;
        String xmlString;
        StringBuilder inputString = new StringBuilder();

        while ((line = reader.readLine()) != null) {
            inputString.append(line);
        }
        xmlString = inputString.toString();

        Map<String, String> map = PayUtils.getMapFromXML(xmlString);
        return map;
    }

    /**
     * 获取支付对象
     *
     * @param paramMap 支付参数（body,out_trade_no,total_fee,ipAddress,userid,username）
     * @return
     */
    public abstract Map<String, String> getPayObject(Map<String, String> paramMap);


    /**
     * 获取二维码支付链接
     *
     * @param paramMap 支付参数（body,out_trade_no,total_fee,ipAddress,userid,username）
     * @return
     */
    public abstract String getPayCodeUrl(Map<String, String> paramMap);

    /**
     * 获取支付信息
     *
     * @param payid 支付id
     */
    public abstract Map<String, String> getOrderInfo(String payid);

    /**
     * 关闭支付订单
     *
     * @param payid 支付id
     */
    public abstract boolean closeOrder(String payid);

    /**
     * 验证支付状态
     *
     * @param payid 支付id
     */
    public abstract Map<String, String> validatePayidState(String payid);

    /**
     * 支付单退款
     *
     * @param map 退款对象（payid:支付ID,aofid:退款ID,total_fee:支付金额,refund_fee:退款金额,userid:操作人ID）
     */
    public abstract boolean refundOrder(Map<String, String> map);

    /**
     * 获取签名信息
     *
     * @param map     签名参数
     * @param strType 返回类型（xml,sign,query）
     */
    public abstract String getSign(Map<String, String> map, String strType);

    public abstract String getSignown(Map<String, Object> map, String strType);

    /**
     * 转帐
     *
     * @param map 付款参数
     *            account:入账账户
     *            realname:账户申请人姓名
     *            payid:支付订单号
     *            amount:金额(元)
     *            ipAddress:申请人ip
     *            desc:转账描述
     * @return 返回（成功或失败）
     */
    public abstract Map<String, String> transfer(Map<String, String> map) throws Exception;

    /**
     * 查询企业余额转帐
     *
     * @param map 查询参数
     * @return 返回（map）
     */
    public abstract Map<String, Object> selTransfer(Map<String, Object> map);

    /**
     * 跳转钱包页
     */
    public abstract String goWallet(Map<String, Object> map);


    public abstract Map<String, Object> payCallback(Map<String, String> map, ActionContext context);

    /**
     * 微信公众号支付，获取支付对象
     */
    public abstract Map<String, String> getPayObjectGzh(Map<String, String> paramMap);

    /**
     * 获取h5支付对象
     *
     * @param paramMap 支付参数（body,out_trade_no,total_fee,ipAddress,userid,username）
     * @return
     */
    public abstract Map<String, String> getPayObjecth5(Map<String, String> paramMap);

}
